#ifndef __DB_HPP__
#define __DB_HPP__
#include "util.hpp"
#include <mutex>
#include <cassert>
#include <string>
using namespace std;



/*用户数据管理模块 -- 用于管理数据库数据，为数据库中的每张表都设计一个类，然后通过类对象来操作数据库表中的数据*/
/*用户信息表*/
class user_table {
public:
    user_table(const std::string &host, const std::string &user, const std::string &passwd, \
                               const std::string db = "mqtt", uint16_t port = 3306) 
    {
        _mysql = mysql_util::mysql_create(host, user, passwd, db, port);
        assert(_mysql != nullptr);
        LOG(DEBUG, "用户数据管理模块初识化完毕");
    }

    ~user_table() {
        if(_mysql != nullptr) { 
            mysql_util::mysql_destroy(_mysql);
            _mysql = nullptr;
        }
        LOG(DEBUG, "用户数据管理模块已被销毁");
    }

    /*数据保存*/
    bool save(Json::Value &user) {
        if(user["a"].isNull()) {
            LOG(NORMAL, "please input a");
            return false;   
        }
        // 由于用户名有唯一键约束，所以不需要担心用户已被注册的情况
        char sql[1024];
#define INSERT_USER "insert into test(a) values('%s')"
        sprintf(sql, INSERT_USER, user["a"].asCString());
        if(mysql_util::mysql_execute(_mysql, sql) == false) {
            LOG(NORMAL, "user register failed");
            return false;
        }
        LOG(NORMAL, "%s 数据库保存成功", user["a"].asCString());
        return true;
    }

    bool count(Json::Value& root){
        char sql[1024];
#define SELECT_COUNT "SELECT (SELECT COUNT(*) FROM peoInfo) AS peoInfo_count,(SELECT COUNT(*) FROM test) AS test_count"
        sprintf(sql, SELECT_COUNT);
        MYSQL_RES *res = nullptr;
        {
            // 加锁
            std::unique_lock<std::mutex> lock(_mutex);
            if(mysql_util::mysql_execute(_mysql, sql) == false) return false;
            // 获取查询到的结果--一行记录
            res = mysql_store_result(_mysql);
            // 注意：当mysql查询结果为空时，mysql_store_result也不会返回空，所以不能在这里判断用户是否存在
            if(res == nullptr) {
                LOG(DEBUG, "mysql store failed: %s", mysql_error(_mysql));
                return false;
            }
        }

        MYSQL_ROW row = mysql_fetch_row(res);
        root["totalNums"] = std::stoi(row[0]);
        root["lackNums"] = std::stoi(row[1]);
        mysql_free_result(res);       
        return true;
    }

    bool select_by_id(const string& id, Json::Value& root) {
#define SELECT_BY_ID "SELECT peoInfo.id, peoInfo.name, test.t FROM test JOIN peoInfo ON test.a = peoInfo.id where id = %s"
        char sql[1024];
        sprintf(sql, SELECT_BY_ID, id.c_str());
        MYSQL_RES *res = nullptr;
        {
            // 加锁
            std::unique_lock<std::mutex> lock(_mutex);
            if(mysql_util::mysql_execute(_mysql, sql) == false) return false;
            // 获取查询到的结果--一行记录
            res = mysql_store_result(_mysql);
            // 注意：当mysql查询结果为空时，mysql_store_result也不会返回空，所以不能在这里判断用户是否存在
            if(res == nullptr) {
                LOG(DEBUG, "mysql store failed: %s", mysql_error(_mysql));
                return false;
            }
        }
        int row_count = mysql_num_rows(res);
        int col_count = mysql_num_fields(res);
        // row_count为0说明查询不到与当前用户名ID匹配的数据，即用户不存在
        if(row_count == 0) {
            LOG(DEBUG, "the user with ID %s does not exist", id.c_str());
            return false;
        }
        
        // 用户名存在唯一键约束
        if(row_count > 1) {
            LOG(ERROR, "there are same user id %s in the database", id.c_str());
            return false;
        }        
        // 填充该用户的其他详细信息
        MYSQL_ROW row = mysql_fetch_row(res);
        root["id"] = row[0];
        root["name"] = row[1];
        root["time"] = row[2];
        mysql_free_result(res); 
        return true;
    }

    /*使用用户姓名查找用户的详细信息*/
    bool select_by_name(const string& name, Json::Value& root) {
#define SELECT_BY_NAME "SELECT peoInfo.id, peoInfo.name, test.t FROM test JOIN peoInfo ON test.a = peoInfo.id where name = '%s'"
        char sql[1024];
        sprintf(sql, SELECT_BY_NAME, name.c_str());
        MYSQL_RES *res = nullptr;
        {
            // 加锁
            std::unique_lock<std::mutex> lock(_mutex);
            if(mysql_util::mysql_execute(_mysql, sql) == false) return false;
            // 获取查询到的结果--一行记录
            res = mysql_store_result(_mysql);
            // 注意：当mysql查询结果为空时，mysql_store_result也不会返回空，所以不能在这里判断用户是否存在
            if(res == nullptr) {
                LOG(DEBUG, "mysql store failed: %s", mysql_error(_mysql));
                return false;
            }
        }
        int row_count = mysql_num_rows(res);
        int col_count = mysql_num_fields(res);
        // row_count为0说明查询不到与当前用户名匹配的数据，即用户不存在
        if(row_count == 0) {
            LOG(DEBUG, "the user with NAME %s does not exist", name.c_str());
            return false;
        }
        
        while(MYSQL_ROW row = mysql_fetch_row(res))
        {
            Json::Value item;
            item["id"] = row[0];
            item["name"] = row[1];
            item["time"] = row[2];
            root.append(item);
        }
        return true;        
    }

    bool select_all_note(Json::Value& root) {
#define SELECT_ALL_NOTE "SELECT peoInfo.id, peoInfo.name, test.t FROM test JOIN peoInfo ON test.a = peoInfo.id"
        char sql[1024];
        sprintf(sql, SELECT_ALL_NOTE);
        MYSQL_RES *res = nullptr;
        {
            // 加锁
            std::unique_lock<std::mutex> lock(_mutex);
            if(mysql_util::mysql_execute(_mysql, sql) == false) return false;
            // 获取查询到的结果--一行记录
            res = mysql_store_result(_mysql);
            // 注意：当mysql查询结果为空时，mysql_store_result也不会返回空，所以不能在这里判断用户是否存在
            if(res == nullptr) {
                LOG(DEBUG, "mysql store failed: %s", mysql_error(_mysql));
                return false;
            }
        }
        int row_count = mysql_num_rows(res);
        int col_count = mysql_num_fields(res);
        // row_count为0说明查询不到与当前用户名ID匹配的数据，即用户不存在
        if(row_count == 0) {
            LOG(DEBUG, "There are currently no records");
            return false;
        }
        
        while(MYSQL_ROW row = mysql_fetch_row(res))
        {
            Json::Value item;
            item["id"] = row[0];
            item["name"] = row[1];
            item["time"] = row[2];
            root.append(item);
        }
        mysql_free_result(res);
        return true;        
    }

    /*查询缺勤名单*/
    bool select_lack(Json::Value& root) {
#define SELECT_LACK "SELECT peoInfo.id, peoInfo.name FROM peoInfo WHERE NOT EXISTS (SELECT 1 FROM test WHERE test.a = peoInfo.id)"
        char sql[1024];
        sprintf(sql, SELECT_LACK);
        MYSQL_RES *res = nullptr;
        {
            // 加锁
            std::unique_lock<std::mutex> lock(_mutex);
            if(mysql_util::mysql_execute(_mysql, sql) == false) return false;
            // 获取查询到的结果--一行记录
            res = mysql_store_result(_mysql);
            // 注意：当mysql查询结果为空时，mysql_store_result也不会返回空，所以不能在这里判断用户是否存在
            if(res == nullptr) {
                LOG(DEBUG, "mysql store failed: %s", mysql_error(_mysql));
                return false;
            }
        }
        int row_count = mysql_num_rows(res);
        int col_count = mysql_num_fields(res);
        if(row_count == 0) {
            LOG(DEBUG, "There are currently no records");
            return false;
        }
        
        while(MYSQL_ROW row = mysql_fetch_row(res))
        {
            Json::Value item;
            item["id"] = row[0];
            item["name"] = row[1];
            item["time"] = row[2];
            root.append(item);
        }
        mysql_free_result(res);
        return true;        
    }
private:
    MYSQL *_mysql;      // mysql操作句柄
    std::mutex _mutex;  // 解决多线程使用同一类对象(句柄)访问数据库时可能发生的线程安全问题
};
#endif
